package com.example.config;

import com.example.realm.AccountRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Hashtable;
import java.util.Map;

/**
 * @author latin-xiao-mao
 * @date 2020/7/8 21:48
 * @description 在这里我们需要配置  3 个 bean ：realm，DefaultWebSecurityManager，ShiroFilterFactoryBean
 * @className ShiroConfig
 */
@Configuration
public class ShiroConfig {

	/**
	 * ********************注意下面的注入 顺序 ******************
	 */


	/**
	 * 最后 将 DefaultWebSecurityManager 注入到 ShiroFilterFactoryBean 中
	 * 通过这三个方法的 bean，最后将这三者都 纳入到 IOC 的管理中了
	 * @return
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){

		// 这里也是同 下面一样，通过 方法名来获取 DefaultWebSecurityManager，从而将其注入到 ShiroFilterFactoryBean 中
		ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
		factoryBean.setSecurityManager(defaultWebSecurityManager);

		// 权限设置
		Map<String, String> map = new Hashtable<>();
		// 访问 main.html，必须是登录认证的
		map.put("/main", "authc");
		// 当访问 manage.html，必须拥有角色 manage，这里是一个数组，表明可以有多个角色
		map.put("/manage", "perms[manage]");
		// 当访问 administrator.html
		map.put("/administrator", "roles[administrator]");

		// 将这个map放入factoryBean中 就会创建三个过滤器链
		factoryBean.setFilterChainDefinitionMap(map);


		// 若不设置，默认跳转到login.jsp
		// 设置登录页面
		factoryBean.setLoginUrl("/login");

		// 如果访问的是未授权页面，则提示用户；设置未授权页面
		factoryBean.setUnauthorizedUrl("/unauth");
		return factoryBean;
	}


	/**
	 * 再 将 Realm 注入到 DefaultWebSecurityManager 中
	 * @return
	 */
	@Bean
	public DefaultWebSecurityManager securityManager(@Qualifier("accountRealm") AccountRealm accountRealm){

		DefaultWebSecurityManager manager = new DefaultWebSecurityManager();

		// 我们现在需要将 下面的 realm 注入到 manager 中
		// 那么怎么注入呢？
		// 其实执行的时候，是先注入 下面的 realm 到 ioc 中，因此我们只需要从 ioc 中获取这个 realm 即可
		// 怎么从 ioc 中获取呢？（要知道realm注入IOC的时候其实默认是按照方法名注入的），因此我们根据方法名即可获取
		// 因此给 defaultWebSecurityManager 方法添加一个参数即可

		// 然后再将这个 realm 注入到 manager 中
		manager.setRealm(accountRealm);

		// 最后将 manager 返回即可
		return manager;
	}


	/**
	 * 先将我们刚才自定义的 realm，注入到 Spring 的 IOC 中，使其纳入ioc管理并生效
	 * @return
	 */
	@Bean
	public AccountRealm accountRealm(){

		return new AccountRealm();
	}
}
